package com.wang.transfer.util.propertiesToJson.treeJson;

import com.wang.transfer.util.propertiesToJson.treeJson.exception.InputFileEmptyException;
import com.wang.transfer.util.propertiesToJson.treeJson.exception.InputOutputFileException;
import com.wang.transfer.util.propertiesToJson.treeJson.exception.OutputFileEmptyException;
import com.wang.common.utils.Utils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.ArrayList;

/**
 * properties内容转换成json格式的内容
 * 针对属性加键值对格式的数据转换成json
 * 使用tree数据结构存储树与节点之间的关系
 * 通过递归一层一层获取子节点，并读入到树中
 * 再递归遍历读出到文件中，一个一个树读取到文件中
 *
 * @author Mr.wang
 * @version 2022.7.26 <strong>1.0.3</strong>
 * @since 2022.7.20
 */
@Component
public class TreeToJson {

    // 判别循环内所有child都走过
    private static Boolean forTag = false;
    // 判别是否存在相同节点
    private static Boolean equalsNode = false;
    // 判断是否修改过行
    private static Boolean editLine = false;
    // 存储tree Json
    private static final StringBuilder s = new StringBuilder();

    // key value 连接符号
    private String SYMBOL = "=";
    // json 缩进
    private String INDENT = "  ";
    // 输入文件
    private String INPUT_FILE = "src/main/resources/prop_tree.properties";
    // 输出文件
    private String OUTPUT_FILE = "src/main/resources/json/prop_tree.json";

    public TreeToJson() {

    }

    public TreeToJson(String inputFilePath, String outputFilePath) throws InputFileEmptyException, OutputFileEmptyException {
        if ("".equals(inputFilePath)) {
            throw new InputFileEmptyException("输入文件路径不能为空！！！");
        }
        if ("".equals(outputFilePath)) {
            throw new OutputFileEmptyException("输出文件路径不能为空！！！");
        }
        this.INPUT_FILE = inputFilePath;
        this.OUTPUT_FILE = outputFilePath;
    }

    /**
     * 配置输入文件路径
     */
    public TreeToJson setReadFilePath(String readFilePath) {
        if (StringUtils.hasText(readFilePath)) {
            this.INPUT_FILE = readFilePath;
        }
        return this;
    }

    /**
     * 配置输出文件路径
     */
    public TreeToJson setWriteFilePath(String writeFilePath) {
        if (StringUtils.hasText(writeFilePath)) {
            this.OUTPUT_FILE = writeFilePath;
        }
        return this;
    }

    /**
     * 配置key-value连接符号
     *
     * @param SYMBOL 连接符号，默认"="，不允许为<code>.</code><br>
     *               <code>.</code>将自动忽略，使用默认连接符号<br>
     */
    public TreeToJson setSYMBOL(String SYMBOL) {
        if (!".".equals(SYMBOL) && !"".equals(SYMBOL) && StringUtils.hasText(SYMBOL)) {
            this.SYMBOL = SYMBOL;
        }
        return this;
    }

    /**
     * 配置缩进格式
     *
     * @param INDENT 缩进字符，默认双空格，支持普通字符串和转义字符串，不允许为空字符串<br>
     *               空字符串将自动忽略，使用默认格式<br>
     */
    public TreeToJson setINDENT(String INDENT) {
        if (!"".equals(INDENT) && StringUtils.hasLength(INDENT)) {
            this.INDENT = INDENT;
        }
        return this;
    }

    /**
     * 执行方法
     */
    public void execute() throws InputOutputFileException {
        JsonList jsonList = new JsonList();
        readFile(jsonList);
        writeFile(jsonList);
    }

    /**
     * 读文件方法
     *
     * @param jsonList 存放jsonTree
     * @since 2022.7.18
     */
    private void readFile(JsonList jsonList) throws InputOutputFileException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(INPUT_FILE));
            String line;
            while ((line = reader.readLine()) != null) {
                line = Utils.checkLine(line, SYMBOL);
                if (line == null) {
                    continue;
                }
                String prefix = line.substring(0, line.indexOf(SYMBOL));
                String suffix = line.substring(line.indexOf(SYMBOL) + 1);
                if ("".equals(prefix)) {
                    // key为空，不处理
                    continue;
                }
                // unicode transfer to chinese
                String lastSuffix = Utils.unicodeToCN(suffix);
                // \替换
                lastSuffix = Utils.replaceSlash(lastSuffix);
                line = line.replace(suffix, lastSuffix);
                Boolean bol = recursionForNodeBol(jsonList, line);
                if (!bol) {
                    // 不存在相同节点，创建
                    JsonTree jsonTree = new JsonTree();
                    createJsonTree(jsonTree, line);
                    jsonList.getJsonTrees().add(jsonTree);
                }
            }
        } catch (IOException e) {
            throw new InputOutputFileException("输入文件不存在！！！");
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 写文件方法
     *
     * @param jsonList 写入的数据
     * @since 2022.7.19
     */
    private void writeFile(JsonList jsonList) throws InputOutputFileException {
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(OUTPUT_FILE));
            // 加入{ 和 换行
            writer.write("{\n");
            ArrayList<JsonTree> jsonTrees = jsonList.getJsonTrees();
            for (JsonTree jsonTree : jsonTrees) {
                writeTree(jsonList, jsonTree, jsonTree, "", 0);
                writer.append(s);
            }
            writer.append("\n}");
        } catch (IOException e) {
            throw new InputOutputFileException("输出文件出错！！！");
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 递归查找是否存在相同节点
     *
     * @return boolean
     */
    private Boolean recursionForNodeBol(JsonList jsonList, String line) {
        for (JsonTree jsonTree : jsonList.getJsonTrees()) {
            String prefix = line.contains(".") ? line.substring(0, line.indexOf(".")) : "";
            recursionForNode(jsonTree, jsonTree, prefix, line, 0);
            if (equalsNode) {
                // 存在相同节点，退出循环
                equalsNode = false;
                return true;
            }
        }
        // 不存在相同节点
        return false;
    }

    /**
     * 递归查找树下是否存在相同节点，有则添加到树下
     *
     * @param fatherNode 父节点
     * @param jsonTree   当前树节点
     * @param oldPrefix  当前树节点的上一个节点名
     * @param line       行
     * @param i          当前树节点在父树节点上的index
     */
    private Boolean recursionForNode(JsonTree fatherNode, JsonTree jsonTree, String oldPrefix, String line, int i) {
        String prefix = line.contains(".") ? line.substring(0, line.indexOf(".")) : "";
        String suffix = line.substring(line.indexOf(".") + 1);
        if (jsonTree.getName().equals(prefix)) {
            // 存在相同的节点，添加，继续往下找
            ArrayList<JsonTree> children = jsonTree.getChildren();
            for (JsonTree child : children) {
                i = children.indexOf(child);
                Boolean bol = recursionForNode(jsonTree, child, prefix, suffix, i);
                if (forTag && i == children.size() - 2 || !forTag && i == children.size() - 1 || editLine || bol) {
                    forTag = false;
                    editLine = false;
                    return true;
                }
            }
        }
        if (fatherNode.getChildren().size() - 1 == i && fatherNode.getName().equals(oldPrefix)) {
            forTag = true;
            equalsNode = true;
            createJsonTree(fatherNode, line);
        }
        return false;
    }

    /**
     * 创建JsonTree
     *
     * @param jsonTree 父树
     * @param line     suffix 下一个节点行
     */
    private void createJsonTree(JsonTree jsonTree, String line) {
        String prefix;
        String suffix;
        boolean checkSuffix;
        // 鉴别 recursionForNode 方法中调用过来的参数
        boolean recCheck = false;
        JsonTree exist = null;
        // 行内. 索引 > SYMBOL
        boolean checkLine = line.indexOf(".") > line.indexOf(SYMBOL);
        if (checkLine && jsonTree.getName() == null) {
            jsonTree.setName(line);
            return;
        } else {
            if (!checkLine) {
                prefix = line.contains(".") ? line.substring(0, line.indexOf(".")) : "";
                suffix = line.substring(line.indexOf(".") + 1);
                suffix = suffix.indexOf(SYMBOL) == suffix.length() - 1 ? suffix + " " : suffix;
            } else {
                prefix = "";
                suffix = line;
                recCheck = true;
            }
            // .的索引大于 SYMBOL 的索引
            checkSuffix = suffix.indexOf(".") > suffix.indexOf(SYMBOL);
        }
        if (prefix.length() == 0 && !checkSuffix || recCheck) {
            for (JsonTree child : jsonTree.getChildren()) {
                String subChild = child.getName().contains(SYMBOL) ? child.getName().substring(0, child.getName().indexOf(SYMBOL)) : "";
                String sufChild = suffix.substring(0, suffix.indexOf(SYMBOL));
                if (subChild.equals(sufChild)) {
                    // 存在相同属性，依后面的值为准
                    exist = child;
                    break;
                }
            }
            // poolServiceAsset=票据池业务-资产业务查询
            if (jsonTree.getName() == null) {
                jsonTree.setName(suffix);
                return;
            }
            // 最后头节点
            JsonTree tree = new JsonTree();
            tree.setName(suffix);
            if (exist != null) {
                jsonTree.getChildren().set(jsonTree.getChildren().indexOf(exist), tree);
                editLine = true;
            } else {
                jsonTree.getChildren().add(tree);
            }
            return;
        }
        JsonTree j;
        if (jsonTree.getName() == null) {
            jsonTree.setName(prefix);
            j = jsonTree;
        } else {
            j = new JsonTree();
            j.setName(prefix);
            jsonTree.getChildren().add(j);
        }
        createJsonTree(j, suffix);
    }

    /**
     * 将每棵树写成json格式
     *
     * @param jsonList   树的集合
     * @param fatherNode 当前节点的父节点
     * @param jsonTree   当前节点
     * @param st         初始化treeJson
     * @param indent     缩进位数
     * @return boolean 是否递归出来 --indent
     * @since 2022.7.20 json格式化
     */
    private Boolean writeTree(JsonList jsonList, JsonTree fatherNode, JsonTree jsonTree, String st, Integer indent) {
        if ("".equals(st)) {
            s.delete(0, s.length());
        }
        String name = jsonTree.getName();
        // 判断Tree是否已结束，是否需要添加 缩进
        boolean bol = false;
        // 判断是否是最后节点
        boolean tag = name.contains(SYMBOL);
        // 判断是否是递归出来的 indent
        boolean b = false;
        // 当前节点是树的最后一个节点
        boolean lastChild = fatherNode.getChildren().indexOf(jsonTree) == fatherNode.getChildren().size() - 1;
        // 当前节点是树集合的最后一个节点
        boolean listLastChild = jsonList.getJsonTrees().indexOf(jsonTree) == jsonList.getJsonTrees().size() - 1;
        String prefix = tag ? name.substring(0, name.indexOf(SYMBOL)) : name;
        String suffix = tag ? name.substring(name.indexOf(SYMBOL) + 1) : "";
        s.append(INDENT).append('"').append(prefix).append('"').append(": ");
        if (!tag) {
            // 不是最后节点，继续往下找
            for (JsonTree child : jsonTree.getChildren()) {
                if (jsonTree.getChildren().indexOf(child) == 0) {
                    // 头节点
                    s.append("{\n").append(Utils.indentN(++indent, INDENT));
                }
                b = writeTree(jsonList, jsonTree, child, s.toString(), indent);
            }
        }
        if (b) --indent;
        if (!"".equals(suffix)) s.append('"').append(suffix).append('"');
        if (lastChild) {
            // 尾节点
            if (fatherNode.getChildren().size() == 0) {
                // 父树没有子节点
                if (!listLastChild) {
                    // 当前树不是树集合的最后一个树
                    s.append(",\n").append(Utils.indentN(indent, INDENT));
                    bol = true;
                }
            } else {
                // 父有子节点
                s.append("\n").append(Utils.indentN(indent, INDENT)).append("}");
            }
        } else if (!listLastChild) {
            // 大括号最后一个不加,
            s.append(",\n");
            bol = true;
        }
        if (bol
                && fatherNode.getChildren().size() > 0
                && !jsonList.getJsonTrees().contains(jsonTree)
                && !lastChild
        ) {
            s.append(Utils.indentN(indent, INDENT));
        }
        return true;
    }
}
